package grammar

import "fmt"
import "strings"
import "regexp"

type Word string

func (w Word) IsNull() bool { return w == "" }

type Item struct
    {
    Name Word
    Precedence int
    }
    
type ItemList struct
    {
    This Item
    Rest *ItemList
    }
    
func (i *ItemList) StartsWith( w Word ) bool {
    return i.FirstWord() == w
}
    
func (i *ItemList) FirstWord() Word {
    if i == nil { return Word("") }
    return i.This.Name
}
        
type Rule struct
    {
    Name Word
    Tag Word
    Rhs *ItemList
    }
    
func (r *Rule) defines( w Word ) bool {
    return r.Name == w
}

func (r *Rule) String() string {
    result := string(r.Name) + " :="
    items := r.Rhs
    for items != nil {
        result = result + " " + string(items.This.Name)
        items = items.Rest
    }
    return result
}
    
type Type struct
    {
    Root *Rule
    Rules []*Rule
    }

func dissect( x string ) (name, tag Word) {
    parts := strings.Split( x, ":", 2 )
    name = Word(parts[0])
    if len(parts) == 1 { tag = name } else { tag = Word(parts[1]) }
    return
}

func itemise( items string ) *ItemList {
    var result *ItemList
    them := strings.Split( items, " ", 0 )
    if len(them) > 0 {
        for i := len(them); i > 0; i -= 1 {
            result = &ItemList{Item{Word(them[i-1]), 0}, result}
        }
    }
    return result
}

func newitemise( items []string ) *ItemList {
    var result *ItemList
    for i := len(items); i > 0; i -= 1 {
        result = &ItemList{Item{Word(items[i-1]), 0}, result}
    }
    return result
}

func ruleFromLine( line string ) (*Rule, bool) {
    fmt.Printf( ";;; looking at line '%v'\n", line )
    idPattern := `[.]?[A-Za-z][A-Za-z0-9]*[+*?]?`
    itemPattern := idPattern + `|\[\$(` + idPattern + `|[0-9]*)\]` + `|::=|"[^"]*"`
    itemRE := regexp.MustCompile( itemPattern )
    items := itemRE.AllMatchesString( line, 0 )
    fmt.Printf( ";;; recognised items %v\n", items )
    if len(items) > 0 {
	    // should be: name opt[tag] ::= item ...
	    name := items[0]
	    tag := name
	    from := 1
	    if items[1][0] == '[' {
	        tag = items[1][1:len(items[1]) - 1]
	        from += 1
	    }
	    from += 1
	    r := &Rule{Word(name), Word(tag), newitemise(items[from:])}
	    fmt.Printf( ">> %v\n", r )
	    return r, true
	} 
	return nil, false
}

func GrammarFromString( s string ) *Type {
    first := ""
    lines := strings.Split( s, "\n", 0 )
    rules := make([]*Rule, 0, len(lines))
    for _, line := range lines {
        if rule, present := ruleFromLine( line ); present {  
            if first == "" { first = string(rule.Name) }  
            rules = rules[0:len(rules)+1]
            rules[len(rules)-1] = rule
        }
    }
    root := &Rule{"$ROOT", "root", &ItemList{Item{Word(first), 0}, nil}}
    return &Type{root, rules}
}
    
func (g *Type) RulesThatDefine( w Word ) []*Rule {
    result := make([]*Rule, 0, len(g.Rules))
    count := 0
    for _, r := range g.Rules {
        if r.defines( w ) {
            result = result[0:count+1]
            result[count] = r
            count += 1
        }
    }
    return result
}
